package com.woniuxy.sddfp.system.service.impl;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.woniuxy.bank.api.access.TradeLogApi;
import com.woniuxy.sddfp.common.constant.UserConstant;
import com.woniuxy.sddfp.common.exception.Assert;
import com.woniuxy.sddfp.common.model.dto.LoginDto;
import com.woniuxy.sddfp.common.model.dto.UserRegisterDto;
import com.woniuxy.sddfp.common.model.dto.bank.BankCardAddDTO;
import com.woniuxy.sddfp.common.model.dto.bank.CardBindDto;
import com.woniuxy.sddfp.common.model.dto.bank.SignatureDto;
import com.woniuxy.sddfp.common.model.vo.LoginVo;
import com.woniuxy.sddfp.common.model.vo.user.UserInfoVo;
import com.woniuxy.sddfp.common.result.enums.impl.BusinessCode;
import com.woniuxy.sddfp.common.result.enums.impl.ResultCode;
import com.woniuxy.sddfp.common.utils.JwtTemplate;
import com.woniuxy.sddfp.common.utils.TokenUtils;
import com.woniuxy.sddfp.system.entity.User;
import com.woniuxy.sddfp.system.entity.UserCard;
import com.woniuxy.sddfp.system.entity.UserDetail;
import com.woniuxy.sddfp.system.mapper.UserMapper;
import com.woniuxy.sddfp.system.service.IUserCardService;
import com.woniuxy.sddfp.system.service.IUserDetailService;
import com.woniuxy.sddfp.system.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author yellow doctor
 * @since 2025-06-23
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private IUserDetailService userDetailService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private JwtTemplate jwtTemplate;

    @Autowired
    private IUserCardService iUserCardService;

    @Autowired
    private TradeLogApi tradeLogApi;

    /**
     *  springboot  中提供的远程调用的类
     */
    @Autowired
    private RestTemplate restTemplate;

    /**
     * 注册
     *   1、校验手机号码  用户名是否存在
     *          存在就抛出异常
     *   2、添加user 和 userDetail
     * @param userRegisterDto
     * //多表操作 添加事务
     *    rollbackFor  回滚指定的异常类型  默认是RuntimeException
     *       NullPointException   NullPointerException extends RuntimeException  这个异常会回滚
     *       IOException          IOException extends Exception    事务失效 （不是继承自 RuntimeException）
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void register(UserRegisterDto userRegisterDto) {
        NullPointerException nullPointException;
        IOException exception;
       //校验手机号码  用户名是否存在
       User user = checkUserByPhone(userRegisterDto.getPhone());

       //boolean  true 抛出异常
        //boolean  和  Boolean 有区别吗?
        Assert.error(user != null, BusinessCode.USER_EXISTED);

        registerUser(userRegisterDto);
    }

    /**
     * 注册用户
     * @param userRegisterDto
     */
    private void registerUser(UserRegisterDto userRegisterDto) {
        //保存用户
        User u = new User();  //添加用户
        u.setPhone(userRegisterDto.getPhone());
        u.setUserName(userRegisterDto.getUserName());
        u.setPwd(userRegisterDto.getPwd());
//        u.setRealName("");     后期给修改
//        u.setIdentityCode(""); 后期修改
        u.setUserType("1");
        u.setUserStatus(false);
        save(u);

        UserDetail userDetail = new UserDetail(); //添加用户详情
        userDetail.setId(u.getId()); //这个id是userId
        userDetail.setUserName(userRegisterDto.getUserName());
        userDetail.setPhone(userRegisterDto.getPhone());
        userDetail.setBorrowMoney(new BigDecimal("0")); //借款金额 默认给0 1

        userDetail.setUserCode("UC_" + UUID.randomUUID().toString().substring(10));  // 用户码 随机搞一个
//        userDetail.setReqCode("");   借款 时候给请求码
        userDetail.setCardBindStatus(false);

        userDetailService.save(userDetail);
    }

    /**
     * 登录
     * @param loginDto
     * @return
     */
    @Override
    public LoginVo login(LoginDto loginDto) {

        Boolean isPhoneLogin = loginDto.getIsPhoneLogin();

        //true  手机登录    false 密码登录
        LoginVo loginVo = isPhoneLogin ? loginByPhone(loginDto):loginByUsername(loginDto);

        return loginVo;
    }

    /**
     *  能根据业务 来实现代码
     *      1、流程  + 表结构字段的修改
     *
     * 用户绑卡：
     * 1、从token中获取userId
     * 2、校验：根据用户id查询u_user_detail用户表，根据CARD_BIND_STATUS字段判断是否已经绑卡
     * 3、如果未绑卡，保存保卡信息到u_user_card表
     * 4、更新用户表：把请求参数中的用户真实姓名、身份证号码更新到u_user表中
     * 5、生成请求流水号，用来访问托管代理，并更新u_user_detail表中流水号字段REQ_CODE
     * 6、远程访问托管代理服务（暂不实现）
     */
    @Override
    public SignatureDto addBankAccount(CardBindDto cardBindDto) {

        //01 从token中获取userId
        String userId = TokenUtils.getTokenUserId();
        System.out.println("userId = " + userId);

        //02 u_user_detail  这个表中的 主键是 user表的id
        UserDetail userDetail = userDetailService.getOne(Wrappers.lambdaQuery(UserDetail.class).eq(
                UserDetail::getId, userId
        ));

        //抛出异常
        Assert.error(userDetail.isCardBindStatus(),BusinessCode.USER_CARD_ALREADY_BINDED);

        //03 保存保卡信息到u_user_card表  先代码生成
        UserCard userCard = new UserCard();
        userCard.setUserId(userDetail.getId());
//        userCard.setBankNo("");    这个后期更新
//        userCard.setBankTitle("");  更新
        userCard.setCardNo(cardBindDto.getCardNo());
        userCard.setPhone(userDetail.getPhone());
        userCard.setStatus(false);

        iUserCardService.save(userCard);

        //04 更新 user表的 id 和 realname
        //update set x1=v1,x2=v2 where id = xx
        update(Wrappers.lambdaUpdate(User.class)
                .set(User::getIdentityCode,cardBindDto.getIdentityCode())
                .set(User::getRealName,cardBindDto.getRealName())
                .eq(User::getId, userId)
        );
        //05 生成请求流水号，用来访问托管代理，并更新u_user_detail表中流水号字段REQ_CODE
        String REQ_CODE = "UC_" + UUID.randomUUID().toString().substring(10); //等着系统给我返回流水号 也可以
        userDetailService.update(
                Wrappers.lambdaUpdate(UserDetail.class)
                        .set(UserDetail::getReqCode,REQ_CODE)
                        .eq(UserDetail::getId, userId)
        );

        //06 进行远程调用  bank-access
        // 执行远程调用  postForEntity 发送post远程调用的方法
        //                           参数1   url
        //                           参数2   需要的请求参数
        //                           参数3   返回的结果
//        String url = "http://localhost:9030/trade-log/bank/access/card";
//        ResponseEntity<SignatureDto> responseEntity =
//                restTemplate.postForEntity(url, bankCardAddDTO, SignatureDto.class);
//        SignatureDto signatureDto = responseEntity.getBody();

        BankCardAddDTO bankCardAddDTO = new BankCardAddDTO();
        bankCardAddDTO.setReqCode(REQ_CODE);
        bankCardAddDTO.setUserId(Long.valueOf(userId));

        SignatureDto signatureDto = tradeLogApi.proxyBindCardLog(bankCardAddDTO);
        System.out.println("代用托管代码 发送数据成功====>signatureDto = " + signatureDto);

        //后期使用openfeign

        return signatureDto;
    }

    @Override
    public UserInfoVo findById(Long userId) {
        // 返回字段分析：user表（userName,identityCode）
        // 返回字段分析：user_detail表（userCode）

        // 根据id查询数据库
        User user = this.getById(userId);
        // 自定义断言判断，减少if判断，让代码更简洁
        Assert.error(user == null, ResultCode.DATA_IS_WRONG);
        // 返回结果
        UserInfoVo userInfoVo = new UserInfoVo();
        userInfoVo.setUserName(user.getUserName());
        userInfoVo.setIdentityCode(user.getIdentityCode());

        // 根据用户id查询用户编码； 用户与用户详情共用主键
        UserDetail userDetail = userDetailService.getById(userId);
        userInfoVo.setUserCode(userDetail.getUserCode());

        return userInfoVo;
    }

    /**
     * 密码登录
     * @param loginDto
     * @return
     */
    private LoginVo loginByUsername(LoginDto loginDto) {

        //01、通过 username 查询用户信息
        String loginName = loginDto.getLoginName();

        Wrappers.lambdaQuery(User.class);
        User user = getOne(Wrappers.lambdaQuery(User.class).eq(User::getUserName, loginName));

        //判断 user是否为null  为null 抛出异常
        Assert.error(user,BusinessCode.USER_NOT_EXIST);

        //02 比对密码

        //抛出异常
        Assert.error(!loginDto.getLoginSecret().equals(user.getPwd()),
                BusinessCode.USER_LOGIN_ERROR);

        //03  返回对象
        return  createLoginVo(user);
    }

    /**
     * 返回一个对象
     * @param user
     * @return
     */
    private LoginVo createLoginVo(User user) {

        //创建token
        Map<String,Object> payLoad = new HashMap<>();
        payLoad.put(UserConstant.USERID,user.getId());

        String token = jwtTemplate.createToken(payLoad);

        // 存储token到redis中： set user_token_userid token
        stringRedisTemplate.opsForValue().set(UserConstant.USER_TOKEN + user.getId(),token, Duration.ofMinutes(300));

        return LoginVo.builder()
                .id(user.getId())
                .userName(user.getUserName())
                .phone(user.getPhone())
                .token(token)
                .build();
    }

    /**
     * 手机号码登录
     * @return
     */
    private LoginVo loginByPhone(LoginDto loginDto) {

        //01 校验验证码

        //从redis 获取验证码
        String codeRedisKey = "user:code:" + loginDto.getLoginName();
        String code = stringRedisTemplate.opsForValue().get(codeRedisKey);

        //抛出异常
        // StringUtils 这个是 spring提供的工具类  字符串的判断工具方法

        //校验验证码
        Assert.error(!code.equals(loginDto.getLoginSecret()),BusinessCode.USER_VERIFY_CODE_ERROR);

        //02 手机号码查询用户
        User user = checkUserByPhone(loginDto.getLoginName());
        //如果是验证码第一次登录  进行注册

        //ObjectUtils  spring提供的工具类 判断对象的方法
        if (ObjectUtils.isEmpty(user)){
            //  如果是第一次需要进行注册
            //注册
            UserRegisterDto userRegisterDto = new UserRegisterDto();
            //随机用户名
            userRegisterDto.setUserName("admin_" + UUID.randomUUID().toString().substring(10));
            userRegisterDto.setPwd("123456");
            //手机号码登录  LoginName 这个是手机号码
            userRegisterDto.setPhone(loginDto.getLoginName());
            registerUser(userRegisterDto);

            //注册用户的对象  第一次登录的用户 返回的对象
            return createLoginVo(
                    getOne(Wrappers.lambdaQuery(User.class)   //查询用户返回
                            .eq(User::getPhone, loginDto.getLoginName())
                    )
            );

        }
        // 验证码登录后 返回之前已存在的对象
        return createLoginVo(user);
    }

    /**
     * 查询用户是否 已存在
     * @param phone
     * @return
     */
    private User checkUserByPhone(String phone) {
        LambdaQueryWrapper<User> queryWrapper = Wrappers.lambdaQuery(User.class).eq(User::getPhone, phone);
        User user = getOne(queryWrapper);
        return user;
    }
}
